#include "gameobjs.h"
#include <vm/value.hpp>
#include "engine.h"

/**
 * \file vm_interface.cpp
 * methods responsible for converting native gameobjects to those recognizable
 * by the virtual machines
 */

using namespace BMD;
using BMD::Value;

Value Engine::construct_vm_input(int bomber_id) const
{
  // construct the bomber list
  vector<Value> bomber_list;
  for (unsigned int i = 0; i < bombers.size(); ++i)
    bomber_list.push_back(bombers[i].to_value());
  ListRef vm_bombers = List::from_vector(bomber_list);

  // bomb list
  vector<Value> bomb_list;
  for (unsigned int i = 0; i < bombs.size(); ++i)
    bomb_list.push_back(bombs[i].to_value(bomber_id));
  ListRef vm_bombs = List::from_vector(bomb_list);

  // powerup list
  vector<Value> powerup_list;
  for (unsigned int i = 0; i < powerups.size(); ++i)
    powerup_list.push_back(powerups[i].to_value());
  ListRef vm_powerups = List::from_vector(powerup_list);

  // tile 2D array
  int height = stage.getH();
  int width = stage.getW();
  BlockRef tiles(new Block(width, Block::Array));
  
  for (int i = 0; i < width; ++i)
  {
    BlockRef col(new Block(height, Block::Array));
    tiles->set_elem(i, Value(col));
    
    for (int j = 0; j < height; ++j)
    {
      col->set_elem(j, Value(to_vm_tile(stage.tile_tinfo(Coordinates(i, j)))));
    }
  }

  // bomber status
  Value bomber_status = bombers[bomber_id].status_value();
  
  // create the input
  vector<Value> vm_input_fields;
  vm_input_fields.push_back(tiles);
  vm_input_fields.push_back(vm_bombers);
  vm_input_fields.push_back(vm_bombs);
  vm_input_fields.push_back(vm_powerups);
  vm_input_fields.push_back(bomber_status);

  return BlockRef(new Block(vm_input_fields, Block::Struct));
}

BomberOrders::BomberOrders(Value v) 
{
  bomb_place = false;
  detonate = false;
  move_direction = dVOID;

  BlockRef b;

  try
  {
    b = v.get_struct();
    if (b->get_size() != 3)
      return;
   
    bomb_place = (bool)b->get_elem(0).get_int();
    detonate = (bool)b->get_elem(1).get_int();
    move_direction = from_vm_direction(b->get_elem(2).get_int());
    
  }
  catch (...)
  {
    cerr << "improper bomber move return, returned: " << endl;
    v.print(cerr, true);
  }
}
    

Value Coordinates::to_value() const 
{
  BlockRef bl(new Block(2, Block::Struct));

  bl->set_elem(0, Value((int)x));
  bl->set_elem(1, Value((int)y));

  return bl;
}

Value Bomb::to_value(int bomber_id) const
{
  vector<Value> fields;

  // position
  fields.push_back( coords.to_value() );
  
  // is_mine
  fields.push_back( Value( bomber_id == get_owner_id() ) );

  // direction
  int dir = mstate.get_direction();
  fields.push_back( Value( dir ) );
  
  return Value(BlockRef(new Block(fields, Block::Struct)));
}

Value Bomber::to_value() const
{
  vector<Value> fields;
  
  // position
  fields.push_back( coords.to_value() );

  // id
  fields.push_back( Value( (int) id ) );
  
  // team id
  fields.push_back( Value( (int) team_id ) );

  return Value(BlockRef(new Block(fields, Block::Struct)));
}

Value PowerUp::to_value() const
{
  vector<Value> fields;
  
  // position
  fields.push_back( coords.to_value() );
  
  // power up type
  fields.push_back( Value( (int) to_vm_powerup(power_type_index)) );

  return Value(BlockRef(new Block(fields, Block::Struct)));
}

Value Bomber::status_value() const
{
  // status
  vector<Value> status_fields;

  // position
  status_fields.push_back(coords.to_value());
  
  // id
  status_fields.push_back(Value((int)id));

  // team id
  status_fields.push_back(Value((int)team_id));
  
  // move rate
  status_fields.push_back(Value(to_vm_movespeed(prop.move_steps)));

  // has kick
  status_fields.push_back(Value(prop.kicking));
  
  // has detonate
  status_fields.push_back(Value(prop.detonation));
  
  // radius
  status_fields.push_back(Value((int)bomb_prop.power));
  
  // max bombs
  status_fields.push_back(Value((int)prop.max_bombs));
  
  // deployed bombs
  status_fields.push_back(Value((int)nbombs));

  return BlockRef(new Block(status_fields, Block::Struct));
}

int to_vm_powerup(int power_up)
{
  return power_up + 1;
}

int to_vm_direction(int direction)
{
  switch (direction)
  {
  case dNORTH: return 1;
  case dEAST: return 2;
  case dSOUTH: return 3;
  case dWEST: return 4;
  default: return 0;
  }
}

int from_vm_direction(int vm_dir)
{
  switch (vm_dir)
  {
  case 1: return dNORTH;
  case 2: return dEAST;
  case 3: return dSOUTH;
  case 4: return dWEST;
  default: return 0;
  }
}

int to_vm_tile(int tile)
{
  return tile + 1;
}
